package mdb

import (
	"database/sql"
	"gitee.com/dennis-mxx/mxx-core-v2/mmodel"
	"gitee.com/dennis-mxx/mxx-core-v2/mutil"
	"xorm.io/builder"
	"xorm.io/xorm"
)

type QueryParam struct {
	Cols   []string
	Page   mmodel.CmdPage
	Sorter *Sorter
	Cond   any
}

type Mapper[E any] struct {
	Session *xorm.Session
	Ent     Entity
	Convert func(fieldName string) string
	Inst    E
}

func NewMapper[E any](session *xorm.Session, entity any) Mapper[E] {
	if ent, ok := entity.(Entity); ok {
		return Mapper[E]{
			Session: session,
			Ent:     ent,
			Convert: func(fieldName string) string {
				return session.Engine().GetTableMapper().Obj2Table(fieldName)
			},
			Inst: entity.(E),
		}
	} else {
		panic("New Mapper failure , entity is not Entity")
	}
}
func DefaultMapper[E any](session *xorm.Session, entity any) Mapper[E] {
	if ent, ok := entity.(Entity); ok {
		return Mapper[E]{
			Session: session,
			Ent:     ent,
			Convert: func(fieldName string) string {
				return fieldName
			},
			Inst: entity.(E),
		}
	} else {
		panic("New Mapper failure , entity is not Entity")
	}
}

func (ew Mapper[E]) SelectPageWatch(param QueryParam) *mmodel.Pageable {
	return mutil.Watch(ew.SelectPage(param))
}

func (ew Mapper[E]) SelectPage(param QueryParam) (*mmodel.Pageable, error) {
	beanList := make([]E, 0)
	return StartPage(ew.Inst, ew.Session, param.Page).Sorter(ew.Convert).Cols(param.Cols).Where(param.Cond).Find(beanList)
}

func (ew Mapper[E]) SelectByID(id any) (*E, error) {

	var bean E
	if exists, err := ew.Session.ID(id).Get(&bean); err == nil {
		if exists {
			return &bean, nil
		} else {
			return nil, nil
		}
	} else {
		return nil, err
	}
}

func (ew Mapper[E]) SelectByIDWatch(id any) *E {
	return mutil.Watch(ew.SelectByID(id))
}

func (ew Mapper[E]) SelectList(param QueryParam) ([]E, error) {
	ses := ew.Session
	if param.Sorter != nil {
		for i, _ := range param.Sorter.descList {
			desc := param.Sorter.descList[i]
			if desc.sortType == "desc" {
				ses.Desc(desc.fieldName)
			} else {
				ses.Asc(desc.fieldName)
			}
		}
	}
	if len(param.Cols) > 0 {
		ses.Cols(param.Cols...)
	}

	beanList := make([]E, 0)
	var err error
	if param.Cond == nil {
		err = ses.Find(&beanList)
	} else {
		err = ses.Where(param.Cond).Find(&beanList)
	}
	if err != nil {
		return nil, err
	} else {
		return beanList, nil
	}

}

func (ew Mapper[E]) SelectListWatch(param QueryParam) []E {
	return mutil.Watch(ew.SelectList(param))
}

func (ew Mapper[E]) Get(entity E) (*E, error) {
	if exists, err := ew.Session.Get(&entity); err == nil {
		if exists {
			return &entity, nil
		} else {
			return nil, nil
		}
	} else {
		return nil, err
	}
}
func (ew Mapper[E]) WatchGet(entity E) *E {
	return mutil.Watch(ew.Get(entity))
}

func (ew Mapper[E]) UpdateByIdWatch(id any, bean E) int64 {
	return mutil.Watch(ew.UpdateById(id, bean))
}
func (ew Mapper[E]) UpdateById(id any, bean E) (int64, error) {
	return ew.Session.ID(id).Update(bean)
}

func (ew Mapper[E]) Insert(bean ...any) (int64, error) {
	return ew.Session.Insert(bean...)
}

func (ew Mapper[E]) InsertWatch(bean ...any) int64 {
	return mutil.Watch(ew.Insert(bean...))
}

func (ew Mapper[E]) InsertMulti(rowsSlicePtr interface{}) (int64, error) {
	return ew.Session.InsertMulti(rowsSlicePtr)
}

func (ew Mapper[E]) InsertMultiWatch(rowsSlicePtr interface{}) int64 {
	return mutil.Watch(ew.Insert(rowsSlicePtr))
}

func (ew Mapper[E]) DeleteByID(tableIdValue any, user mmodel.SPUser) (bool, error) {
	num, err := ew.Delete(builder.In(ew.Ent.IdName(), tableIdValue), user)
	return num > 0, err
}

func (ew Mapper[E]) DeleteByIDWatch(tableIdValue any, user mmodel.SPUser) bool {
	return mutil.Watch(ew.DeleteByID(tableIdValue, user))
}
func (ew Mapper[E]) Delete(cond any, user mmodel.SPUser) (int64, error) {
	t := mmodel.NowLocalDateTime()
	return ew.Session.Table(ew.Ent.TableName()).
		Where(cond).
		Update(map[string]any{
			"deleted":          t,
			"update_time":      t,
			"update_user_code": user.UserCode,
			"update_user_name": user.UserName,
		})
}

func (ew Mapper[E]) DeleteWatch(cond any, user mmodel.SPUser) int64 {
	return mutil.Watch(ew.Delete(cond, user))
}

func (ew Mapper[E]) Exec(sqlOrArgs ...interface{}) (sql.Result, error) {
	return ew.Session.Exec(sqlOrArgs...)
}
func (ew Mapper[E]) ExecWatch(sqlOrArgs ...interface{}) sql.Result {
	return mutil.Watch(ew.Exec(sqlOrArgs...))
}
